#include "types.h"
#include "platform.h"

/*
 * The UART control registers are memory-mapped at address UART0. 
 * This macro returns the address of one of the registers.
 */
#define UART_REG(reg) ((volatile uint8_t *)(UART0 + reg))


/*
 * UART control registers map. see [1] "PROGRAMMING TABLE"
 * note some are reused by multiple functions
 * 0 (write mode): THR/DLL
 * 1 (write mode): IER/DLM
 */
#define RHR 0	// Receive Holding Register (read mode)
#define THR 0	// Transmit Holding Register (write mode)
#define DLL 0	// LSB of Divisor Latch (write mode)
#define IER 1	// Interrupt Enable Register (write mode)
#define DLM 1	// MSB of Divisor Latch (write mode)
#define FCR 2	// FIFO Control Register (write mode)
#define ISR 2	// Interrupt Status Register (read mode)
#define LCR 3	// Line Control Register
#define MCR 4	// Modem Control Register
#define LSR 5	// Line Status Register
#define MSR 6	// Modem Status Register
#define SPR 7	// ScratchPad Register

/*
 * POWER UP DEFAULTS
 * IER = 0: TX/RX holding register interrupts are both disabled
 * ISR = 1: no interrupt penting
 * LCR = 0
 * MCR = 0
 * LSR = 60 HEX
 * MSR = BITS 0-3 = 0, BITS 4-7 = inputs
 * FCR = 0
 * TX = High
 * OP1 = High
 * OP2 = High
 * RTS = High
 * DTR = High
 * RXRDY = High
 * TXRDY = Low
 * INT = Low
 */

/*
 * LINE STATUS REGISTER (LSR)
 * LSR BIT 0:
 * 0 = no data in receive holding register or FIFO.
 * 1 = data has been receive and saved in the receive holding register or FIFO.
 * ......
 * LSR BIT 5:
 * 0 = transmit holding register is full. 16550 will not accept any data for transmission.
 * 1 = transmitter hold register (or FIFO) is empty. CPU can load the next character.
 * ......
 */
#define LSR_RX_READY (1 << 0)
#define LSR_TX_IDLE  (1 << 5)

#define uart_read_reg(reg) (*(UART_REG(reg)))
#define uart_write_reg(reg, v) (*(UART_REG(reg)) = (v))

void uart_init()
{
	/* disable interrupts. */
	uart_write_reg(IER, 0x00);

	/*
	 * Setting baud rate. Just a demo here if we care about the divisor,
	 * but for our purpose [QEMU-virt], this doesn't really do anything.
	 *
	 * Notice that the divisor register DLL (divisor latch least) and DLM (divisor
	 * latch most) have the same base address as the receiver/transmitter and the
	 * interrupt enable register. To change what the base address points to, we
	 * open the "divisor latch" by writing 1 into the Divisor Latch Access Bit
	 * (DLAB), which is bit index 7 of the Line Control Register (LCR).
	 *
	 * Regarding the baud rate value, see [1] "BAUD RATE GENERATOR PROGRAMMING TABLE".
	 * We use 38.4K when 1.8432 MHZ crystal, so the corresponding value is 3.
	 * And due to the divisor register is two bytes (16 bits), so we need to
	 * split the value of 3(0x0003) into two bytes, DLL stores the low byte,
	 * DLM stores the high byte.
	 */
	uint8_t lcr = uart_read_reg(LCR);
	uart_write_reg(LCR, lcr | (1 << 7));
	uart_write_reg(DLL, 0x03);
	uart_write_reg(DLM, 0x00);

	/*
	 * Continue setting the asynchronous data communication format.
	 * - number of the word length: 8 bits
	 * - number of stop bits：1 bit when word length is 8 bits
	 * - no parity
	 * - no break control
	 * - disabled baud latch
	 */
	lcr = 0;
	uart_write_reg(LCR, lcr | (3 << 1));
}

int uart_putc(char ch)
{
	while ((uart_read_reg(LSR) & LSR_TX_IDLE) == 0);
	return uart_write_reg(THR, ch);
}

void uart_puts(char *s)
{
	while (*s) {
		uart_putc(*s++);
	}
}

/**
 * 读取一个字符
 */
int uart_getc()
{
    char ch;

    // 等待接收缓冲区有数据
    while ((uart_read_reg(LSR) & LSR_RX_READY) == 0);

    // 读取接收到的字符
    ch = uart_read_reg(RHR);
    return ch;
}

/**
 * 读取一行字符串，直到遇到回车符 '\r'，支持删除键处理
 */
void uart_gets(char *s, int len)
{
    int i = 0;
    char ch;

    while (i < len - 1) {
        ch = uart_getc(); // 读取一个字符

        if (ch == '\r') { // 如果是回车符，结束读取
            break;
        } else if (ch == '\b' || ch == 127) { // 处理删除键（'\b' 或 ASCII 127）
            if (i > 0) {
                i--; // 从缓冲区中移除最后一个字符
                uart_putc('\b'); // 回显删除键
                uart_putc(' ');  // 用空格覆盖已删除的字符
                uart_putc('\b'); // 将光标移回一格
            }
        } else {
            s[i++] = ch; // 存储字符
            uart_putc(ch); // 回显输入的字符
        }
    }
    s[i] = '\0'; // 添加字符串结束符
}

/**
 * 回显功能：读取用户输入并回显到控制台
 */
void uart_echo()
{
    char buffer[100]; // 缓冲区，用于存储输入的字符串

    uart_puts("UART Echo Ready:\r\n"); // 提示信息
    while (1) {
        uart_gets(buffer, sizeof(buffer)); // 读取一行输入
        uart_putc('\r'); // 回车
        uart_putc('\n'); // 换行
        uart_puts("Kernel received: "); // 输出提示信息
        uart_puts(buffer); // 输出回显的字符串
        uart_putc('\r'); // 回车
        uart_putc('\n'); // 换行
    }
}
